use alloc::{string::String, sync::Arc};
use crossbeam_queue::ArrayQueue;
use spin::{rwlock::RwLock, Lazy};
use syscall::scheme::{KernelScheme, SchemeRef, SchemeTy};

const SCANCODE_QUEUE_SIZE: usize = 128;

static SCANCODE_QUEUE: Lazy<ArrayQueue<u8>> = Lazy::new(|| ArrayQueue::new(SCANCODE_QUEUE_SIZE));

pub fn add_scancode(scancode: u8) {
    if let Err(_) = SCANCODE_QUEUE.push(scancode) {
        log::warn!("Scancode queue full, dropping keyboard input!");
    }
}

/// Return the scan code of the keyboard buffer, returns None is the buffer is empty.
pub fn get_scancode() -> Option<u8> {
    SCANCODE_QUEUE.pop()
}

/// Return whether the keyboard buffer is empty.
pub fn has_scancode() -> bool {
    !SCANCODE_QUEUE.is_empty()
}

pub struct KBDFileScheme {
    path: String,
}

impl KBDFileScheme {
    pub fn new() -> SchemeRef {
        let scheme = Arc::new(RwLock::new(Self {
            path: String::new(),
        }));
        scheme
    }
}

impl KernelScheme for KBDFileScheme {
    fn when_mounted(&mut self, path: String, _father: Option<SchemeRef>) {
        self.path.clear();
        self.path.push_str(path.as_str());
    }

    fn when_umounted(&mut self) {}

    fn get_path(&self) -> String {
        self.path.clone()
    }

    fn read_at(&self, _offset: usize, buf: &mut [u8]) -> usize {
        let mut read_len: usize = 0;
        while read_len < buf.len() {
            if let Some(scancode) = get_scancode() {
                buf[read_len] = scancode;
                read_len += 1;
            } else {
                while !has_scancode() {
                    crate::context::schedule();
                }
            }
        }
        read_len
    }

    fn size(&self) -> usize {
        SCANCODE_QUEUE.len()
    }

    fn scheme_type(&self) -> SchemeTy {
        SchemeTy::File
    }
}
